package org.oschina.security.impl;

import org.apache.commons.lang.StringUtils;
import org.oschina.Constants;
import org.oschina.Module;
import org.oschina.api.user.role.service.UserRoleService;
import org.oschina.api.user.user.UserType;
import org.oschina.api.user.user.dao.UserDAO;
import org.oschina.api.user.user.model.User;
import org.oschina.api.user.user.service.UserService;
import org.oschina.bean.Result;
import org.oschina.helper.EncryptHelper;
import org.oschina.helper.JsonHelper;
import org.oschina.helper.ThreadVariableHelper;
import org.oschina.helper.messagequeue.IMessageQueueHelper;
import org.oschina.security.AuthenticationException;
import org.oschina.security.AuthenticationInfo;
import org.oschina.security.ISecurity;
import org.oschina.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class SecurityImpl implements ISecurity {
	
	private final static Logger logger=LoggerFactory.getLogger(SecurityImpl.class);
	
	@Autowired
	private EncryptHelper encryptHelper;
	@Autowired
	private UserRoleService roleService;
	@Autowired
	private IMessageQueueHelper messageQueueHelper;
	@Autowired
	private ThreadVariableHelper threadVariableHelper;
	@Autowired
	private UserService userService;
	@Autowired
	private UserDAO userDAO;

	@Override
	public Object getAuthenticationInfo(String token)
	{
		if(StringUtils.isBlank(token))
			return null;
		
		TokenBean tokenBean=parseToken(token);
		if(tokenIsTimeout(tokenBean))
			return null;
		
		String threadTokenKey=Constants.THREAD_VARIABLE_AUTHENTICATION+"_"+token;
		User user=(User)threadVariableHelper.get(threadTokenKey);
		if(user==null)
		{
			user=(User) userDAO.getOnlineCache(tokenBean.getId());
			
			if(user==null)
			{
				user=userDAO.findByToken(token);
				if(user==null)
					return null;
				
				Result loginResult=userService.login(user.getAccount(), passwordFormat_decrypt(user.getPassword()));
				if(!loginResult.isSuccess())
					return null;
				
				return getAuthenticationInfo(token);
			}
			else
			{
				long now=DateUtil.now();
				if(user.getLast_post()>=0 && now-user.getLast_post()>Constants.REFRESH_ONLINE_USER_SECURITY)
					refreshCache(user,now);
				
				threadVariableHelper.set(threadTokenKey, user);
			}
		}
		
		return user;
	}
	
	@Override
	public AuthenticationInfo login(AuthenticationInfo authenticationInfo) throws AuthenticationException
	{
		User user=userDAO.findByAccountAndType(authenticationInfo.getUsername(),UserType.SIMPLE);
		
		if(user==null)
			throw new UserNotFoundException();
		
		//没有登录权限
		if(roleService._findVoByRoleAndModule(user, Integer.parseInt(Module.MODULE_1))==null)
			throw new AuthenticationException();
		
		//密码错误
		if(!user.getPassword().equals(passwordFormat(authenticationInfo.getPassword())))
			throw new PasswordErrorException();
		
		//用户已在线
		if(userDAO.isOnline(user.getId()))
			throw new UserIsOnlineException();
		
		TokenBean tokenBean=parseToken(user.getToken());
		String token=user.getToken();
		if(tokenIsTimeout(tokenBean))
		{
			token=getToken(user);
			userDAO.updateToken(user.getId(), token);
		}
		
		authenticationInfo.setToken(token);
		
		user.setOnline(true);
		user.setIp(threadVariableHelper.get(Constants.THREAD_VARIABLE_IP).toString());
		user.setLast_login(DateUtil.now());
		
		putCache(user);
		refreshCache(user);
		userDAO.updateOnline(user.getId(), true);
		messageQueueHelper.send(Constants.QUEUE_USER_STATUS_REFRESH, user.getId());
		
		logger.info("{} - 已登录",authenticationInfo.getUsername());
		return authenticationInfo;
	}

	@Override
	public boolean logout(String token)
	{
		User user=getAuthenticationUser(token);
		if(user==null)
			return false;
		
		refreshCache(user,-1);
		return true;
	}

	@Override
	public boolean require(String authorities[], String token, boolean or)
	{
		User user=getAuthenticationUser(token);
		if(user==null)
			return false;
		
		int passCount=0;
		for(String authority:authorities)
		{
			if(roleService._findVoByRoleAndModule(user, Integer.parseInt(authority))!=null)
			{
				passCount++;
				if(or)
					break;
			}
		}
		
		return (passCount==authorities.length || (passCount>0 && or));
	}
	
	@Override
	public String passwordFormat(String password)
	{
		return encryptHelper.encrypt(password);
	}
	
	private String passwordFormat_decrypt(String password)
	{
		return encryptHelper.decrypt(password);
	}
	
	private void putCache(User user)
	{
		userDAO.putOnlineCache(user);
	}
	
	private void refreshCache(User user)
	{
		refreshCache(user,DateUtil.now());
	}
	
	private void refreshCache(User user,long time)
	{
		userDAO.refreshLastPostCache(user.getId(), time);
	}
	
	private User getAuthenticationUser(String token)
	{
		return (User)getAuthenticationInfo(token);
	}
	
	private String getToken(User user)
	{
		TokenBean tokenBean=new TokenBean(user.getId(),DateUtil.now());
		return encryptHelper.encrypt(JsonHelper.toJson(tokenBean));
	}
	
	private TokenBean parseToken(String token)
	{
		if(StringUtils.isBlank(token))
			return null;
		
		return (TokenBean) JsonHelper.parse(encryptHelper.decrypt(token),TokenBean.class);
	}
	
	private boolean tokenIsTimeout(TokenBean tokenBean)
	{
		if(tokenBean==null || tokenBean.getStart_time()==null)
			return true;
		
		if(DateUtil.now()-tokenBean.getStart_time()>Constants.TIMEOUT_USER_TOKEN)
			return true;
		
		return false;
	}
}